﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interactivity;
using System.Windows;
using System.ComponentModel;
using System.Windows.Markup;
using System.Windows.Shapes;
using System.Windows.Media;
using XWord.WPF.ApplicationFramework.Extensions;
using System.Windows.Data;
namespace XWord.WPF.Behaviors
{
    public class PropertyTrigger : TriggerBase<FrameworkElement>
    {
        public string PropertyName { get; set; }

        public object Value { get; set; }
        private TypeConverter _converter;
    
        public object PropertyValue
        {
            get { return (object)GetValue(PropertyValueProperty); }
            set { SetValue(PropertyValueProperty, value); }
        }

        protected override void OnAttached()
        {
            var binding = new Binding(PropertyName) {Source = AssociatedObject};
            BindingOperations.SetBinding(this, PropertyValueProperty, binding);
            base.OnAttached();
        }

        public static readonly DependencyProperty PropertyValueProperty =
            DependencyProperty.Register("PropertyValue", typeof(object), typeof(PropertyTrigger), 
            new UIPropertyMetadata(new PropertyChangedCallback((sender,args)=>
                                                                   {
                                                                       var trigger = sender as PropertyTrigger;
                                                                       if(args.NewValue!=null)
                                                                       trigger.PropertyChangedHandle(null, null);
                                                                   })));

        private void PropertyChangedHandle(object sender, EventArgs e)
        {
            if (_converter == null) _converter = TypeDescriptor.GetConverter(PropertyValue.GetType());
            if (PropertyValue.Equals(_converter.ConvertFrom(Value)))
            {
                InvokeActions(SetterMode.Default);
            }
            else
            {
                InvokeActions(SetterMode.RollBack);
            }
        }
    }

    public class VisualSetter : DpSetterBase
    {
        public string TargetName { get; set; }
        public Type TargetType { get; set; }


        public override void SetElement()
        {
            AssociatedObject.Loaded += AssociatedObject_Loaded;
        }

        void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
        {
            if (VisualTreeHelper.GetChildrenCount(AssociatedObject) > 0)
            {
                this.Dispatcher.BeginInvoke(new Action(() =>
                {
                    FindVisualElement();
                    this.SetRollBackValue();
                }), System.Windows.Threading.DispatcherPriority.Render);

                AssociatedObject.Loaded -= AssociatedObject_Loaded;
            }
        }
        protected override void Invoke(SetterMode mode)
        {
            FindVisualElement();
        }

        private void FindVisualElement()
        {
            if (Element != null) return;
            if (string.IsNullOrEmpty(TargetName))
                Element = AssociatedObject.FindVisualChild(TargetType);
            else
                Element = AssociatedObject.FindVisualChild(TargetName);
        }
    }

    public class DpSetter : DpSetterBase
    {
        public override void SetElement()
        {
            Element = this.AssociatedObject;
        }
    }

    public abstract class DpSetterBase : TriggerAction<FrameworkElement>
    {
        public SetterMode Mode { get; set; }

        public DependencyProperty Property { get; set; }

        private object _rollBackValue;

        public virtual void SetElement() { }
        protected void SetRollBackValue()
        {
            this.Dispatcher.BeginInvoke(new Action(() =>
            {
                _rollBackValue = Element.GetValue(Property);
            }), System.Windows.Threading.DispatcherPriority.Render);

        }

        protected FrameworkElement Element { get; set; }



        protected override void OnAttached()
        {
            SetElement();
            if (Mode == SetterMode.RollBack && Element != null)
            {
                SetRollBackValue();
            }
            base.OnAttached();
        }

        public virtual void RollBack()
        {
            if (Element!=null)
            {
                Element.SetValue(Property, _rollBackValue);
            }
            
        }

        [DependsOn("Property")]
        public object Value { get; set; }

        private TypeConverter _converter;
        protected TypeConverter Converter
        {
            get
            {
                return _converter ?? (_converter = TypeDescriptor.GetConverter(Property.PropertyType));
            }
        }
        protected override void Invoke(object parameter)
        {
            var currentMode = (SetterMode)parameter;

            if (currentMode == SetterMode.Default)
            {
                Invoke(currentMode);
                while (Element == null) { }
                Element.SetValue(Property, TransValue());
            }
            else
            {
                if (Mode == SetterMode.RollBack)
                {
                    this.RollBack();
                }   
            }
        }

        private object TransValue()
        {
            if (Value.GetType().IsSubclassOf(Property.PropertyType) || Value.GetType().Equals(Property.PropertyType))
                return Value;
            return Converter.ConvertFrom(Value);
        }

        private void DirectSetValue()
        {
            if (Value.GetType().Equals(Property.PropertyType))
                Element.SetValue(Property, Value);
        }

        protected virtual void Invoke(SetterMode mode)
        {
        }
    }

    public enum SetterMode
    {
        Default,
        RollBack
    }
}
